feat: implement modular domain architecture with DTO pattern#2
Merged
feat: implement modular domain architecture with DTO pattern#2
Conversation
Restructure the project to use a modular domain-driven architecture where each business domain is self-contained with its own domain, usecase, repository, and HTTP layers. This improves scalability, maintainability, and team collaboration for larger projects. BREAKING CHANGE: Package structure has changed from flat layers to domain-based modules. Import paths need to be updated for any external consumers. Changes: - Reorganize internal/ structure into domain modules (user/, outbox/) - Move user entities from internal/domain to internal/user/domain - Move user use cases from internal/usecase to internal/user/usecase - Move user repository from internal/repository to internal/user/repository - Move user HTTP handler from internal/http to internal/user/http - Move outbox entities from internal/domain to internal/outbox/domain - Move outbox repository from internal/repository to internal/outbox/repository - Update all imports to use aliased domain-specific imports - Update main.go to use new import paths with aliases - Update HTTP server to use user HTTP handler from new location - Update worker to use outbox domain from new location - Update all test files with new import paths - Refactor response utilities in internal/http for shared use - Update README.md with new architecture documentation Benefits: - Easy to add new domains without affecting existing code - Clear domain boundaries and encapsulation - Related code is co-located within domain modules - Better support for team collaboration on different domains - Scalable structure suitable for growing applications New structure: internal/ ├── config/ # Shared configuration ├── database/ # Shared database infrastructure ├── http/ # Shared HTTP server and middleware ├── outbox/ # Outbox domain module │ ├── domain/ │ └── repository/ ├── user/ # User domain module │ ├── domain/ │ ├── http/ │ ├── repository/ │ └── usecase/ └── worker/ # Shared background worker
Extract JSON response handling into a dedicated httputil package to promote code reuse and eliminate duplication across domain modules. This allows all HTTP handlers to use a consistent, public API for response formatting. Changes: - Create new internal/httputil package for shared HTTP utilities - Move MakeJSONResponse from internal/http to internal/httputil - Make MakeJSONResponse function public (capitalized) - Add comprehensive tests for httputil package - Update internal/http/response.go to wrap httputil.MakeJSONResponse - Update internal/http/middleware.go to use MakeJSONResponse - Update internal/user/http/user_handler.go to use httputil.MakeJSONResponse - Remove duplicate makeJSONResponse implementation from user handler - Update internal/http/http_test.go to use httputil.MakeJSONResponse - Update README.md with httputil documentation and usage examples Benefits: - Single source of truth for JSON response handling - Public API accessible from any package (no import cycles) - Eliminates code duplication across domain modules - Consistent response formatting across all endpoints - Well-tested shared utility with 100% coverage - Clear separation of concerns (utilities vs infrastructure)
Introduce Data Transfer Objects (DTOs) to enforce clear boundaries between internal domain models and external API contracts. This architectural improvement enhances security, maintainability, and flexibility. Changes: - Create internal/user/http/dto package with request, response, and mapper - Add RegisterUserRequest DTO with validation logic - Add UserResponse DTO excluding sensitive fields (password) - Add mapper functions to convert between DTOs and domain/usecase models - Update user_handler.go to use DTOs instead of exposing domain models - Remove JSON tags from User domain entity (internal/user/domain/user.go) - Update tests to use DTOs for API requests and responses - Add comprehensive DTO pattern documentation to README.md Benefits: - Domain models now purely represent internal business entities - Sensitive fields never exposed in API responses - API contracts can evolve independently from domain models - Request validation happens at the DTO level - Enables multiple API versions with different DTOs - Improved security through explicit field mapping The User entity no longer has JSON serialization tags, making it a true domain model decoupled from presentation concerns. HTTP handlers now use purpose-built DTOs that explicitly define the API contract.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Complete architectural refactoring to establish a scalable, maintainable
codebase using modular domain-driven design principles with proper
separation of concerns between internal models and API contracts.
Key Changes:
Benefits:
BREAKING CHANGE: Import paths have changed from internal/{domain,usecase,
repository,http} to internal/{domain}/{domain,usecase,repository,http}.
External consumers must update imports.